package com.aleyn.processor.scanner

import com.aleyn.annotation.Autowired
import com.aleyn.annotation.Factory
import com.aleyn.annotation.Route
import com.aleyn.annotation.Singleton
import com.aleyn.processor.data.RouterMeta
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.validate

/**
 * @author : Aleyn
 * @date : 2023/07/23 : 17:01
 *
 * LRouter 元数据扫描器。
 * 这是 KSP（Kotlin Symbol Processing）注解处理器的核心部分，其主要职责是在编译期间执行以下任务：
 * 1.  **扫描注解**：查找项目中所有使用了 LRouter 相关注解的符号，包括：
 *     - `@Route`：用于标记一个可路由的页面（Activity、Fragment）。
 *     - `@Autowired`：用于在目标页面中标记需要自动注入的属性。
 *     - `@Singleton` / `@Factory`：用于定义依赖注入容器中的对象实例（单例或工厂）。
 * 2.  **验证符号**：确保被注解标记的符号是有效的，例如，注解是否用在了正确的位置。
 * 3.  **转换元数据**：将被注解的符号信息转换成 LRouter 内部定义的、结构化的元数据对象（如 `RouterMeta`）。
 *
 * 这些元数据是后续代码生成步骤的唯一输入，代码生成器将依据这些元数据来创建路由表、依赖注入辅助类等。
 */

/**
 * 定义了用于模块内依赖注入的注解列表。
 * KSP 处理器会扫描所有被这些注解标记的类，并将它们注册到依赖注入容器中。
 */
val MODULE_INJECT = listOf(Singleton::class, Factory::class)

/**
 * LRouter 元数据扫描器类。
 * @param logger KSP 提供的日志记录器，用于在编译过程中输出信息、警告或错误。
 */
class LRouterMetaDataScanner(
    private val logger: KSPLogger
) {

    // 内部持有一个 ClassInjectScanner 实例，专门用于处理 `@Singleton` 和 `@Factory` 注解的类
    private val injectClassScanner = ClassInjectScanner(logger)

    // 用于分类存储扫描过程中发现的、经过验证的有效 KSP 符号
    private var validRouterSymbols = mutableListOf<KSAnnotated>() // 存储 @Route 注解的符号
    private var validInjectedSymbols = mutableListOf<KSAnnotated>() // 存储 @Singleton/@Factory 注解的符号
    private var validAutowiredSymbols = mutableListOf<KSAnnotated>() // 存储 @Autowired 注解的符号

    /**
     * 扫描并验证所有与 LRouter 相关的注解符号。
     *
     * 这个函数是 KSP 处理器 `process` 方法的核心调用之一。它使用 `Resolver` 来获取所有被
     * `@Route`, `@Autowired`, `@Singleton`, `@Factory` 注解标记的符号，并对它们进行验证。
     * 有效的符号会被分类存储起来，无效的符号则会被收集并返回，同时通过 logger 输出警告。
     *
     * @param resolver KSP 的 `Resolver` 实例，提供了访问和解析编译器符号的 API。
     * @return 返回所有无效符号的列表。如果所有符号都有效，则此列表为空。
     */
    fun scanSymbols(resolver: Resolver): List<KSAnnotated> {

        // 1. 获取并验证所有依赖注入相关的注解符号 (@Singleton, @Factory)
        val moduleInjectSymbols = MODULE_INJECT.flatMap { annotation ->
            resolver.getSymbolsWithAnnotation(annotation.qualifiedName!!)
        }.groupBy { it.validate() } // 使用 KSP 的 validate() 函数进行验证，并按验证结果分组

        // 2. 获取并验证所有 @Autowired 注解的符号
        val autowiredSymbols = resolver.getSymbolsWithAnnotation(Autowired::class.qualifiedName!!)
            .groupBy { it.validate() }

        // 3. 获取所有 @Route 注解的符号。根据 KSP 的建议，@Route 的验证可以稍微延迟。
        validRouterSymbols.addAll(resolver.getSymbolsWithAnnotation(Route::class.qualifiedName!!))

        // 4. 将验证通过的符号添加到各自的内部列表中，以备后续处理
        moduleInjectSymbols[true]?.let { validInjectedSymbols.addAll(it) }
        autowiredSymbols[true]?.let { validAutowiredSymbols.addAll(it) }

        // 5. 收集所有在验证中失败的符号
        val invalidInject = moduleInjectSymbols[false] ?: emptyList()
        val invalidAutowired = autowiredSymbols[false] ?: emptyList()
        val allInvalid = invalidInject + invalidAutowired
        // 如果存在无效符号，则通过 logger 记录警告，并返回这些符号
        if (allInvalid.isNotEmpty()) return allInvalid.onEach { logger.warn("Invalid symbols: $it") }

        logger.logging("All symbols are valid")
        return emptyList()
    }

    /**
     * 将扫描到的路由和注入信息整合成一个代表当前模块的元数据对象。
     *
     * @return [RouterMeta.Module] 一个包含了当前模块所有路由规则 (`@Route`)
     *         和依赖注入定义 (`@Singleton`/`@Factory`) 的元数据集合。
     */
    fun getModuleDeclaration(): RouterMeta.Module {
        val router = arrayListOf<RouterMeta.ModuleRouter>()

        logger.logging("scan router ...")

        // 从已验证的路由符号中，创建 ModuleRouter 元数据列表
        validRouterSymbols
            .filterIsInstance<KSClassDeclaration>() // 只处理类声明
            .mapNotNull { it.createModuleRouter() } // 使用扩展函数将 KSClassDeclaration 转换为 ModuleRouter
            .forEach(router::add)

        logger.logging("scan definitions ...")

        // 从已验证的注入符号中，创建依赖注入的 Definition 元数据列表
        val definition = validInjectedSymbols
            .filterIsInstance<KSClassDeclaration>()
            .map { injectClassScanner.createClassDefinition(it) } // 委托给 injectClassScanner 处理
            .toList()

        // 将路由列表和注入定义列表封装成一个 Module 元数据对象
        return RouterMeta.Module(router, definition)
    }

    /**
     * 获取所有需要进行依赖注入的类的声明。
     *
     * 这个函数处理所有被 `@Autowired` 标记的属性，并将它们按所属的类进行分组，
     * 最终为每个包含 `@Autowired` 属性的类生成一个 `RouterMeta.RouterAutowired` 元数据对象。
     *
     * @return [List<RouterMeta.RouterAutowired>] 一个元数据列表，其中每个元素代表一个需要进行依赖注入的类及其所有相关属性。
     */
    fun getAutowiredDeclaration(): List<RouterMeta.RouterAutowired> {
        return validAutowiredSymbols
            .asSequence()
            .filterIsInstance<KSPropertyDeclaration>() // 序列的起点是所有 @Autowired 标记的属性
            .mapNotNull { it.parentDeclaration } // 获取每个属性所在的父级声明（即类声明）
            .toSet() // 通过 toSet() 对类声明进行去重，确保每个类只处理一次
            .filterIsInstance<KSClassDeclaration>()
            .mapNotNull { it.createAutowiredClass() } // 为每个类创建一个 RouterAutowired 元数据
            .toList()
    }

}